///PHILOTES Source Code.  (C)2012 PhiloLabs
#pragma once

#ifndef __CompositorInstance_H__
#define __CompositorInstance_H__

#include "OgrePrerequisites.h"
#include "OgreMaterialManager.h"
#include "OgreTexture.h"
#include "OgreRenderQueue.h"
#include "OgreCompositionTechnique.h"

namespace Ogre {
	
	
	const size_t RENDER_QUEUE_COUNT = RENDER_QUEUE_MAX+1;       
            
    
	class _OgreExport CompositorInstance : public MemAlloc_Resource
    {
    public:
        CompositorInstance(CompositionTechnique *technique, CompositorChain *chain);
        virtual ~CompositorInstance();
		
		class _OgreExport Listener
		{
		public:
			virtual ~Listener();

			
			virtual void notifyMaterialSetup(uint32 pass_id, MaterialPtr &mat);

			
			virtual void notifyMaterialRender(uint32 pass_id, MaterialPtr &mat);

			
			virtual void notifyResourcesCreated(bool forResizeOnly);
			
		};
        
		class _OgreExport RenderSystemOperation : public MemAlloc_Resource
		{
		public:
			virtual ~RenderSystemOperation();
			/// Set state to SceneManager and RenderSystem
			virtual void execute(SceneManager *sm, RenderSystem *rs) = 0;
		};
		typedef map<int, MaterialPtr>::type QuadMaterialMap;
		typedef std::pair<int, RenderSystemOperation*> RenderSystemOpPair;
		typedef vector<RenderSystemOpPair>::type RenderSystemOpPairs;
        
        class TargetOperation
        {
        public:
            TargetOperation()
            { 
            }
            TargetOperation(RenderTarget *inTarget):
                target(inTarget), currentQueueGroupID(0), visibilityMask(0xFFFFFFFF),
                lodBias(1.0f),
                onlyInitial(false), hasBeenRendered(false), findVisibleObjects(false), 
				materialScheme(MaterialManager::DEFAULT_SCHEME_NAME), shadowsEnabled(true)
            { 
            }
            /// Target
            RenderTarget *target;

			/// Current group ID
			int currentQueueGroupID;

			/// RenderSystem operations to queue into the scene manager, by
			/// uint8
			RenderSystemOpPairs renderSystemOperations;

			/// Scene visibility mask
            /// If this is 0, the scene is not rendered at all
            uint32 visibilityMask;
            
            /// LOD offset. This is multiplied with the camera LOD offset
            /// 1.0 is default, lower means lower detail, higher means higher detail
            float lodBias;
            
            
            typedef std::bitset<RENDER_QUEUE_COUNT> RenderQueueBitSet;

			/// Which renderqueues to render from scene
			RenderQueueBitSet renderQueues;
            
            
            bool onlyInitial;
            
            bool hasBeenRendered;
            
            bool findVisibleObjects;
			
			String materialScheme;
			
			bool shadowsEnabled;
        };
        typedef vector<TargetOperation>::type CompiledState;
        
        
        void setEnabled(bool value);
        
        
		bool getEnabled() const { return mEnabled; }

		
        void setAlive(bool value);

        
		bool getAlive() const { return mAlive; }

		
		const String& getTextureInstanceName(const String& name, size_t mrtIndex);

		
		TexturePtr getTextureInstance(const String& name, size_t mrtIndex);

		
		RenderTarget* getRenderTarget(const String& name);

       
        
        virtual void _compileTargetOperations(CompiledState &compiledState);
        
        
        virtual void _compileOutputOperation(TargetOperation &finalState);
        
        
        Compositor *getCompositor();
        
        
        CompositionTechnique *getTechnique();

		
		void setTechnique(CompositionTechnique* tech, bool reuseTextures = true);

		
		void setScheme(const String& schemeName, bool reuseTextures = true);

		/// Returns the name of the scheme this compositor is using
		const String& getScheme() const { return mTechnique ? mTechnique->getSchemeName() : StringUtil::BLANK; }

		
		void notifyResized();

		
        CompositorChain *getChain();

		
		void addListener(Listener *l);

		
		void removeListener(Listener *l);

		
		void _fireNotifyMaterialSetup(uint32 pass_id, MaterialPtr &mat);

		
		void _fireNotifyMaterialRender(uint32 pass_id, MaterialPtr &mat);

		
		void _fireNotifyResourcesCreated(bool forResizeOnly);
	private:
        /// Compositor of which this is an instance
        Compositor *mCompositor;
        /// Composition technique used by this instance
        CompositionTechnique *mTechnique;
        /// Composition chain of which this instance is part
        CompositorChain *mChain;
        /// Is this instance enabled?
        bool mEnabled;
		/// Is this instance allocating resources?
        bool mAlive;
        /// Map from name->local texture
        typedef map<String,TexturePtr>::type LocalTextureMap;
        LocalTextureMap mLocalTextures;
		/// Store a list of MRTs we've created
		typedef map<String,MultiRenderTarget*>::type LocalMRTMap;
		LocalMRTMap mLocalMRTs;
		typedef map<CompositionTechnique::TextureDefinition*, TexturePtr>::type ReserveTextureMap;
		
		ReserveTextureMap mReserveTextures;

		/// Vector of listeners
		typedef vector<Listener*>::type Listeners;
		Listeners mListeners;
        
        /// Previous instance (set by chain)
        CompositorInstance *mPreviousInstance;
		
		
        virtual void collectPasses(TargetOperation &finalState, CompositionTargetPass *target);
        
        
        MaterialPtr createLocalMaterial(const String& srcName);
        
        
        void createResources(bool forResizeOnly);
        
        
        void freeResources(bool forResizeOnly, bool clearReserveTextures);

        
        RenderTarget *getTargetForTex(const String &name);
        
        
        const String &getSourceForTex(const String &name, size_t mrtIndex = 0);

		
		void queueRenderSystemOp(TargetOperation &finalState, RenderSystemOperation *op);

		/// Util method for assigning a local texture name to a MRT attachment
		String getMRTTexLocalName(const String& baseName, size_t attachment);

		
		void deriveTextureRenderTargetOptions(const String& texname, 
			bool *hwGammaWrite, uint *fsaa, String* fsaaHint);

		/// Notify this instance that the primary viewport's camera has changed.
		void notifyCameraChanged(Camera* camera);

        friend class CompositorChain;
    };
	
	

}

#endif
